﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.CompilerServices;

namespace blackhouse.BlackApart.Business.Prices
{

    internal class ImpApartmentPriceProfile : IApartmentPrice
    {

        #region Fields

        private List<PriceProfileVariant> variants = new List<PriceProfileVariant>();
        private List<PriceProfileModification> mods = new List<PriceProfileModification>();
        private bool priceCalculated = false;
        private int reservationNumberOfPeoples;
        private DateTime reservationFromDay;
        private DateTime reservationToDay;
        private double price = 0;
        private double priceWithoutRabat = 0;
        private float rabat = 0;

        #endregion

        #region Properties

        public int ReservationNumberOfPeoples
        {
            get { return this.reservationNumberOfPeoples; }
            set
            {
                if (this.reservationNumberOfPeoples != value)
                {
                    priceCalculated = false;
                    this.reservationNumberOfPeoples = value;
                }
            }
        }

        public DateTime ReservationFromDay
        {
            get { return this.reservationFromDay; }
            set
            {
                if (this.reservationFromDay != value)
                {
                    priceCalculated = false;
                    this.reservationFromDay = value;
                }
            }
        }

        public DateTime ReservationToDay
        {
            get { return this.reservationToDay; }
            set
            {
                if (this.reservationToDay != value)
                {
                    priceCalculated = false;
                    this.reservationToDay = value;
                }
            }
        }

        #endregion

        #region Constructors

        public ImpApartmentPriceProfile() { }

        #endregion

        #region Methods

        private void CalculatePrices()
        {
            PriceProfileVariant variant = this.GetVariant();
            if (variant == null)
            {
                this.price = 0;
                this.priceWithoutRabat = 0;
                this.priceCalculated = true;
                return;
            }

            this.priceWithoutRabat = variant.GetPrice(this.ReservationNumberOfPeoples);
            if (variant.DisableRestModifications)
            {
                this.price = this.priceWithoutRabat;
            }
            else
                this.SetPricesToModification();
            this.SetPricesToUpAllStay();
            this.price = Math.Round(this.price, 2);
            this.priceWithoutRabat = Math.Round(this.priceWithoutRabat, 2);
            this.rabat = Convert.ToSingle(Math.Round(100d * (this.price / this.priceWithoutRabat - 1), 2));
            this.priceCalculated = true;
        }

        private void SetPricesToModification()
        {
            double percentMod = 0;
            double valueMod = 0;
            foreach (PriceProfileModification mod in this.GetApplicableModifications())
            {
                if (mod.ModificationType == PriceProfileModificationType.Percent)
                    percentMod += mod.ModificationValue;
                if (mod.ModificationType == PriceProfileModificationType.Value)
                    valueMod += mod.ModificationValue;
            }
            this.price = this.priceWithoutRabat + valueMod;
            this.price = this.price * (100 + percentMod) / 100;
        }

        private void SetPricesToUpAllStay()
        {
            double lengthOfStay = (this.ReservationToDay.Date - this.ReservationFromDay.Date).TotalDays;
            this.price = this.price * lengthOfStay;
            this.priceWithoutRabat = this.priceWithoutRabat * lengthOfStay;
        }

        private IEnumerable<PriceProfileModification> GetApplicableModifications()
        {
            return this.mods.Where(m => m.IsApply(this.ReservationFromDay, this.ReservationToDay, this.ReservationNumberOfPeoples));
        }

        private PriceProfileVariant GetVariant()
        {
            var q = from v in variants
                    where v.FromDate <= this.ReservationFromDay
                    && v.ToDate >= this.ReservationToDay
                    select v;
            q = from v in q
                orderby (v.ToDate - v.FromDate).Ticks
                select v;
            return q.FirstOrDefault();
        }

        public double GetPrice()
        {
            if (!this.priceCalculated) this.CalculatePrices();
            return this.price;
        }

        public double GetPriceWithoutRabat()
        {
            if (!this.priceCalculated) this.CalculatePrices();
            return this.priceWithoutRabat;
        }

        public float GetRabat()
        {
            if (!this.priceCalculated) this.CalculatePrices();
            return this.rabat;
        }

        public void AddVariant(PriceProfileVariant variant)
        {
            this.priceCalculated = false;
            this.variants.Add(variant);
        }

        public void AddModification(PriceProfileModification mod)
        {
            this.priceCalculated = false;
            this.mods.Add(mod);
        }

        #endregion

    }
}
